package mysql

import (
	"database/sql"
	"go.uber.org/zap"
	"struggle/model"
)

type articleDAO struct{}

var DefaultArticle = articleDAO{}

// Create 添加文章
func (a articleDAO) Create(article *model.Article) (err error) {
	// 涉及处理多表，需要使用事务
	tx, err := db.Begin()
	if err != nil {
		zap.L().Error("mysql transaction failed", zap.Error(err))
		return
	}
	defer ClearTransaction(tx) // 如果出现异常情况,导致没有 commit和rollback,可以用来收尾

	// 1. 新增 文章表 内容
	sqlStr := `INSERT INTO st_article (sn, title, uid, cover, content, tags, words, state) values (?, ?, ?, ?, ?, ?, ?, ?)`
	_, err = tx.Exec(sqlStr, article.Sn, article.Title, article.UID, article.Cover, article.Content, article.Tags, article.Words, 1)
	if err != nil {
		zap.L().Error("crate article failed", zap.Any("article", article), zap.Error(err))
		return
	}
	// 2. 新增一条 文章扩展表
	articleEx := &model.ArticleEx{
		ArticleSn: article.Sn,
	}
	sqlStrEx := `INSERT INTO st_article_ex (article_sn) values (?)`
	_, err = tx.Exec(sqlStrEx, articleEx.ArticleSn)
	if err != nil {
		zap.L().Error("crate article_ex failed", zap.Any("article", article), zap.Error(err))
		return
	}
	// 3. 更新用户计数表
	if err = IncreaseArticle(article.UID, article.Words); err != nil {
		return
	}

	// 事务提交
	if err := tx.Commit(); err != nil {
		zap.L().Error("commit article failed", zap.Any("article", article), zap.Error(err))
	}
	return
}

// Update 更新文章
func (a articleDAO) Update(article *model.Article) (err error) {
	sqlStr := `UPDATE st_article SET title=?, uid=?, cover=?, content=?, tags=?, words=?, state=? where sn = ?`
	_, err = db.Exec(sqlStr, article.Title, article.UID, article.Cover, article.Content, article.Tags, article.Words, article.State, article.Sn)
	if err != nil {
		zap.L().Error("update article failed", zap.Any("article", article), zap.Error(err))
		return
	}
	return
}

// Find 获取所有已上线的文章列表
func (a articleDAO) Find(offset, limit int) (articleInfoList []*model.ArticleInfo, err error) {
	sqlStr := `SELECT * 
			FROM 
			     st_article JOIN st_article_ex ON st_article.sn = st_article_ex.article_sn 
			WHERE state=1 order by aid desc limit ?, ?`
	err = db.Select(&articleInfoList, sqlStr, offset, limit)
	if err != nil {
		zap.L().Error("get article list failed", zap.Error(err))
		return
	}
	return
}

// FindUserArticle 获取所有个人文章列表
func (a articleDAO) FindUserArticle(sn uint64, offset, limit int) (articleInfoList *[]model.ArticleInfo, err error) {
	articleInfoList = &[]model.ArticleInfo{}
	sqlStr := `SELECT * 
			FROM 
			     st_article JOIN st_article_ex ON st_article.sn = st_article_ex.article_sn 
			WHERE state=1 and uid= ? order by aid desc limit ?, ?`
	err = db.Select(articleInfoList, sqlStr, sn, offset, limit)
	if err != nil {
		zap.L().Error("get user article list failed", zap.Error(err))
		return
	}
	return
}

// FindOne 不仅获取 Article，还需要 ArticleEx 信息
func (a articleDAO) FindOne(sn uint64) (articleInfo *model.ArticleInfo, err error) {
	article := &model.Article{}
	sqlStr := `SELECT aid, sn, title, uid, cover, content, tags, words, state, created_at, updated_at from st_article where sn = ?`
	err = db.Get(article, sqlStr, sn)
	if err != nil {
		zap.L().Error(" FindOneArticle failed", zap.Any("article", article), zap.Error(err))
		return
	}
	articleEx := &model.ArticleEx{}
	sqlStr2 := `SELECT article_sn, view_num, cmt_num, zan_num, updated_at FROM st_article_ex WHERE article_sn = ?`
	err = db.Get(articleEx, sqlStr2, sn)
	if err != nil {
		zap.L().Error("FindOneArticle failed", zap.Any("articleEx", articleEx), zap.Error(err))
		return
	}
	articleInfo = &model.ArticleInfo{
		Article:   *article,
		ArticleEx: *articleEx,
	}
	return
}

// FindArticle 只是获取 Article，不需要 ArticleEx 信息
func (a articleDAO) FindArticle(sn uint64) (article *model.Article, err error) {
	article = &model.Article{}
	sqlStr := `SELECT aid, sn, title, uid, cover, content, tags, words, state, created_at, updated_at from st_article where sn = ?`
	err = db.Get(article, sqlStr, sn)
	if err != nil {
		zap.L().Error("get article failed", zap.Error(err))
		return
	}
	return
}

// GetArticleCount 获取文章总数
func (a articleDAO) GetArticleCount() (articleCount int, err error) {
	sqlStr := `SELECT count(aid) from st_article`
	err = db.Get(&articleCount, sqlStr)
	if err != nil {
		zap.L().Error("GetArticleCount failed", zap.Error(err))
		return
	}
	return
}

func ClearTransaction(tx *sql.Tx) {
	err := tx.Rollback()
	if err != sql.ErrTxDone && err != nil {
		zap.L().Error("mysql transaction failed, 事务回滚rollback也失败了", zap.Error(err))
	}
}

// UpdateExView 增加文章扩展表的浏览数字段
func (a articleDAO) UpdateExView(sn uint64, count int) (err error) {
	sqlStr := `UPDATE st_article_ex SET view_num = ? where article_sn = ?`
	_, err = db.Exec(sqlStr, count, sn)
	if err != nil {
		zap.L().Error("update article_ex view num failed", zap.Error(err))
		return
	}
	return
}

// UpdateExZan 更新文章扩展表字段 称赞的数量
func (a articleDAO) UpdateExZan(sn uint64, count int) error {
	sqlStr := `UPDATE st_article_ex SET zan_num = ? WHERE article_sn = ?`
	_, err := db.Exec(sqlStr, count, sn)
	if err != nil {
		zap.L().Error("update article_ex zan num failed", zap.Error(err))
		return err
	}
	return err
}

// 根据文章编号返回作者编号
func (a articleDAO) GetAuthor(sn uint64) (author uint64, err error) {
	sqlStr := `SELECT uid FROM st_article WHERE sn = ? LIMIT 1`
	err = db.Get(&author, sqlStr, sn)
	if err != nil {
		zap.L().Error("according article sn find user sn failed", zap.Error(err))
		return
	}
	return
}

// GetAuthorZan 返回该用户所有文章赞的总数
func (a articleDAO) GetAuthorZan(sn uint64) (count int, err error) {
	sqlStr := `SELECT sum(ex.zan_num) FROM	st_article as a, st_article_ex as ex WHERE a.sn = ex.article_sn AND	a.uid=?`
	err = db.Get(&count, sqlStr, sn)
	if err != nil {
		zap.L().Error("according user sn find zan sn failed", zap.Error(err))
		return
	}
	return
}
